package io.wongd.lib_douyin.download

import android.content.ContentValues
import android.content.Context
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import okhttp3.ResponseBody
import java.io.File

data class UriCase(
    val uri: Uri,
    val file: File,
)

/**
 * 获取视频媒体库uri
 *
 * @param environmentType 只能是[Environment.DIRECTORY_PICTURES]或[Environment.DIRECTORY_DCIM]
 * 或[Environment.DIRECTORY_MOVIES]
 */
fun obtainVideoMediaUri(
    context: Context,
    filename: String,
    fileTypeSuffix: String = ".mp4",
    parentPathname: String = "DouYinDownloader",
    environmentType: String = Environment.DIRECTORY_MOVIES,
): UriCase {
    val parentPath = Environment.getExternalStoragePublicDirectory(environmentType).path +
            File.separator + parentPathname + File.separator
    val realFilename = "${filename}_${System.currentTimeMillis()}$fileTypeSuffix"
    println(">>>> real $realFilename")
    val contentValues = ContentValues()
    contentValues.put(MediaStore.Video.VideoColumns.DISPLAY_NAME, realFilename)
    contentValues.put(MediaStore.Video.VideoColumns.MIME_TYPE, "video/mp4")
    contentValues.put(MediaStore.Video.VideoColumns.DATE_ADDED, System.currentTimeMillis())
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        contentValues.put(
            MediaStore.Video.VideoColumns.RELATIVE_PATH,
            environmentType + File.separator + parentPathname
        )
    } else {
        contentValues.put(MediaStore.Video.VideoColumns.DATA, parentPath + realFilename)
    }
    val newUri = try {
        context.contentResolver
            .insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues)
    } catch (e: Exception) {
        println(">>>> err ${e.message}")
        null
    }
    return UriCase(newUri!!, File(parentPath, realFilename))
}

data class DownloadTask(
    val filename: String,
    val fileLinkUrl: String,
)

sealed interface DownloadStatus {
    object Idle : DownloadStatus
    data class Progress(val percent: Int) : DownloadStatus
    data class Complete(val success: Boolean) : DownloadStatus
}

val JoinDownloadTaskFlow = MutableSharedFlow<DownloadTask>(extraBufferCapacity = 1)

fun ResponseBody.downloadFileWithProgress(context: Context, uri: Uri, onFailure: suspend (Throwable) -> Unit = {}): Flow<DownloadStatus> = flow {
    emit(DownloadStatus.Progress(0))
    var success: Boolean
    kotlin.runCatching {
        context.contentResolver.openOutputStream(uri).use { outputStream ->
            byteStream().use { inputStream ->
                val totalBytes = contentLength()
                val buffer = ByteArray(4 * 1024)
                var progressBytes = 0L
                while (true) {
                    val byteCount = inputStream.read(buffer)
                    if (byteCount == -1) break
                    outputStream?.write(buffer, 0, byteCount)
                    progressBytes += byteCount
                    val percent = ((progressBytes * 100) / totalBytes).toInt()
                    emit(DownloadStatus.Progress(percent))
                }
                when {
                    progressBytes < totalBytes -> {
                        success = false
                        onFailure(Throwable("download failed -> missing bytes"))
                    }

                    progressBytes > totalBytes -> {
                        success = false
                        onFailure(Throwable("download failed -> too many bytes"))
                    }

                    else -> success = true
                }
            }
        }
        emit(DownloadStatus.Complete(success))
    }.onFailure {
        onFailure(it)
    }
}.flowOn(Dispatchers.IO).distinctUntilChanged()